import boto3
import os
from typing import BinaryIO, Optional, List, Dict
import asyncio
from functools import partial

class S4Client:
    """
    一个用于与S4兼容的对象存储服务交互的类。
    它将同步的boto3调用包装在异步方法中，以避免阻塞事件循环。
    """
    def __init__(self, bucket_name: str):
        self._client = boto3.client(
            's3',
            aws_access_key_id=settings.S3_ACCESS_KEY,
            aws_secret_access_key=settings.S3_SECRET_KEY,
            endpoint_url=settings.S3_ENDPOINT,
            region_name=settings.S3_REGION,
        )
        self.bucket_name = bucket_name or settings.S3_BUCKET_NAME_MAIN
        logger.info(f"StoreService initialized for bucket '{self.bucket_name}' at endpoint '{settings.S3_ENDPOINT}'.")

    async def _run_in_thread(self, func, *args, **kwargs):
        """
        一个辅助方法，在单独的线程中运行同步函数，使其不阻塞asyncio事件循环。
        """
        # functools.partial 用于创建一个新的可调用对象，它像原始函数一样，但预先填充了部分参数。
        # 这对于将带参数的函数传递给 to_thread 非常有用。
        p_func = partial(func, *args, **kwargs)
        # asyncio.to_thread 在一个独立的线程中运行 p_func 并等待其结果。
        return await asyncio.to_thread(p_func)

    async def list_objects(self) -> List[Dict]:
        """异步列出存储桶中的对象。"""
        try:
            response = await self._run_in_thread(self._client.list_objects_v2, Bucket=self.bucket_name)
            return response.get('Contents', [])
        except Exception as e:
            logger.error(f"Failed to list objects in bucket '{self.bucket_name}': {e}")
            raise BusinessError(ErrorType.STORAGE_ERROR, "Failed to list objects.")

    async def upload_fileobj(self, file_obj: BinaryIO, object_name: str) -> bool:
        """异步上传文件对象 (如 FastAPI 的 UploadFile.file)。"""
        try:
            await self._run_in_thread(self._client.upload_fileobj, file_obj, self.bucket_name, object_name)
            logger.info(f"Successfully uploaded to {self.bucket_name}/{object_name}")
            return True
        except Exception as e:
            logger.error(f"Failed to upload to {self.bucket_name}/{object_name}: {e}")
            raise BusinessError(ErrorType.STORAGE_ERROR, "File upload failed.")

    async def download_file(self, object_name: str, file_path: str) -> bool:
        """异步下载一个对象到本地文件。"""
        try:
            await self._run_in_thread(self._client.download_file, self.bucket_name, object_name, file_path)
            logger.info(f"Successfully downloaded {self.bucket_name}/{object_name} to {file_path}")
            return True
        except Exception as e:
            logger.error(f"Failed to download {self.bucket_name}/{object_name}: {e}")
            raise BusinessError(ErrorType.STORAGE_ERROR, "File download failed.")

    async def get_presigned_url(self, object_name: str, expires_in: int = 3600) -> str:
        """异步生成一个对象的预签名URL。"""
        try:
            url = await self._run_in_thread(
                self._client.generate_presigned_url,
                'get_object',
                Params={'Bucket': self.bucket_name, 'Key': object_name},
                ExpiresIn=expires_in
            )
            return url
        except Exception as e:
            logger.error(f"Failed to generate presigned URL for {self.bucket_name}/{object_name}: {e}")
            raise BusinessError(ErrorType.STORAGE_ERROR, "Failed to get file URL.")
